package com.study.springbootsecurity.filter.sms;

import com.study.springbootsecurity.entity.LoginUser;
import com.study.springbootsecurity.entity.business.SmsCodeAuthenticationToken;
import com.study.springbootsecurity.util.RedisCache;
import com.study.springbootsecurity.util.SmsUtil;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.util.Collection;

/**
 * @Description: TODO 这个类作用处理登录逻辑
 * @author: Huangjianyun
 * @date: 2025-06-14 19:18
 */
@Slf4j
public class SmsCodeAuthenticationProvider implements AuthenticationProvider, MessageSourceAware {
    // 权限映射器，用于转换和合并用户权限
    private final GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
    // 用户详情服务，用于加载用户信息
    private final UserDetailsService userDetailsService;
    // 消息源访问器，用于国际化错误消息
    private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    private final RedisCache redisCache;
    private static final String SMS_CODE_KEY_PREFIX= "sms:code:";
    public SmsCodeAuthenticationProvider(UserDetailsService userDetailsService, RedisCache redisCache) {
        this.userDetailsService = userDetailsService;
        this.redisCache = redisCache;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 验证令牌类型
        Assert.isInstanceOf(SmsCodeAuthenticationToken.class,authentication,
                () -> messages.getMessage(
                        "SmsAuthenticationProvider.onlySupports",
                        "Only SmsAuthenticationToken is supported"));

        // 判断手机号不能为空
        SmsCodeAuthenticationToken smsCodeAuthToken = (SmsCodeAuthenticationToken) authentication;
        // 获取手机号
        String phone = (String) smsCodeAuthToken.getPrincipal();
        //SecurityContextHolder.getContext().setAuthentication(smsCodeAuthToken);

        if (StringUtils.isBlank(phone.trim())) {
            throw new BadCredentialsException(messages.getMessage("手机号不能为null"));
        }
        phone = phone.trim();
        //校验验证码
        String smsCodeKey = SMS_CODE_KEY_PREFIX + phone;
        String storeSmsCode  = redisCache.getCacheObject(smsCodeKey);
        // 获取验证码
        String smsCode = (String) smsCodeAuthToken.getCredentials();
        boolean validSmsCodeResult = new SmsUtil().validSmsCode(smsCode, storeSmsCode);
        LoginUser loginUser = (LoginUser) userDetailsService.loadUserByUsername(phone);
        if (ObjectUtils.isEmpty(loginUser)) {
            throw new BadCredentialsException("手机验证码登录用户不存在");
        }
        // 检查账号是否过期 是否启用 是否锁定
        check(loginUser);
        // 构建自定义访问令牌
        if (validSmsCodeResult) {
            return createSuccessAuthentication(smsCodeAuthToken, loginUser);
        }else {
            throw new BadCredentialsException("验证码校验失败");
        }

    }

    /**
     * 判断authentication是否SmsCodeAuthenticationToke的子类或者接口
     * @param authentication 认证token对象
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
    }

    @Override
    public void setMessageSource(MessageSource messageSource) {
        this.messages = new MessageSourceAccessor(messageSource);
    }

    /**
     * 检查用户是否锁定 账号是否过期 是否启用
     * @param user 用户详情业务接口
     */
    public void check(UserDetails user) {
        if (!user.isAccountNonLocked()) {
            log.debug("Failed to authenticate since user account is locked");
            throw new LockedException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked"));
        } else if (!user.isEnabled()) {
            log.debug("Failed to authenticate since user account is disabled");
            throw new DisabledException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled"));
        } else if (!user.isAccountNonExpired()) {
            log.debug("Failed to authenticate since user account has expired");
            throw new AccountExpiredException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired"));
        }
    }

    /**
     * 认证成功将非授信凭据转为授信凭据.
     * 封装用户信息 角色信息。
     *
     * @param authentication the authentication
     * @param user           the user
     * @return the authentication
     */
    protected Authentication createSuccessAuthentication(Authentication authentication, UserDetails user) {

        Collection<? extends GrantedAuthority> authorities = authoritiesMapper.mapAuthorities(user.getAuthorities());
        SmsCodeAuthenticationToken authenticationToken = new SmsCodeAuthenticationToken(user, null, authorities);
        authenticationToken.setDetails(authentication.getDetails());

        return authenticationToken;
    }

}
